package hamsterwrench.data.reload;

import hamsterwrench.OHRRPGCE;
import java.io.*;
import java.util.ArrayList;

import hamsterwrench.data.reload.ReloadNode;

public class ReloadDataManager {
	/*	private DataInputStream din;
//	private DataOutputStream don;
//	private LumpDataManager ldm;
//	private boolean WritingMode = false;

//	public ReloadDataManager(InputStream i){
//		din = new DataInputStream(i);
//	}
//	
//	public ReloadDataManager(OutputStream o){
//		don = new DataOutputStream(o);
//		WritingMode = true;
	}*/

	public synchronized static ReloadNode readReloadFile(File in){
		RandomAccessFile din;
		LittleEndianRAF ler;
		try {
			din = new RandomAccessFile(in, "r");
		} catch (FileNotFoundException e1) {
			System.err.println("ERROR: " + in.getName() + " wasn't found.");
			e1.printStackTrace();
			return null;
		}
		ler = new LittleEndianRAF(din);
		ReloadNode result = null;

		//Begin preamble check
		try{
			for(int i = 0; i < 4; i++){
				if((char)din.read() != OHRRPGCE.RELOAD_HEADER[i]){
					System.err.println("ERROR: " + in.getName() + " doesn't look like a RELOAD file.");
					return null;
				}
			}

			int versionCheck = din.readUnsignedByte();
			if(versionCheck != OHRRPGCE.RELOAD_CURRENT_VERSION){
				System.err.println("ERROR: Can't handle RELOAD version " + versionCheck + ".");
				return null;
			}

			int headerLenCheck = ler.readInt();
			if(headerLenCheck != OHRRPGCE.RELOAD_CURRENT_HEADER_SIZE){
				System.err.println("ERROR: Found header length " + headerLenCheck + ", was expecting " + OHRRPGCE.RELOAD_CURRENT_HEADER_SIZE);
				return null;
			}

			int stringTableOffset = ler.readInt();
			System.out.println("RELOAD file looks sane, node name offset is " + stringTableOffset);

			result = readIn(din, stringTableOffset, ler);
			din.close();
		}catch(IOException e){
			e.printStackTrace();
		}

		return result;
	}

	public static void writeReloadFile(File out, ReloadNode relTree){
		RandomAccessFile dot;
		LittleEndianRAF ler;
		ArrayList<String> names = new ArrayList<String>();
		int stringTableLocation;

		try{
			dot = new RandomAccessFile(out, "rw");
		}catch (FileNotFoundException e){
			e.printStackTrace();
			return;
		}
		ler = new LittleEndianRAF(dot);

		try{
			for(int i = 0; i<4; i++){
				dot.write(OHRRPGCE.RELOAD_HEADER[i]);
			}
			dot.write(OHRRPGCE.RELOAD_CURRENT_VERSION);
			ler.writeInt(OHRRPGCE.RELOAD_CURRENT_HEADER_SIZE);
			ler.writeInt(0); // We don't know what the string table offset will be yet, we'll be going back to position 9 after we write the data.
			
			readOut(dot, ler, relTree, names);
			stringTableLocation = (int)dot.getFilePointer();
			writeVLI(dot, names.size());
			for(int i = 1; i <= names.size(); i++){
				writeVLI(dot, i);
				for(int j = 0; j < names.get(i).length(); j++){
					dot.write((byte)names.get(i).codePointAt(j));
				}
			}
			dot.seek(9);
			ler.writeInt(stringTableLocation);
			dot.close();
		}catch(IOException e){
			e.printStackTrace();
		}
	}



	private static ReloadNode readIn(RandomAccessFile din, int stringTableLocation, LittleEndianRAF ler) throws IOException{
		ReloadNode result = null;
		
		System.out.println("position " + Long.toHexString(din.getFilePointer()));

		//report data size
		int nodeLength = ler.readInt();
		System.out.println("I see a RELOAD node of length " + nodeLength);
		//get node name ID
		int nameID = readVLI(din);

		//get the type
		int type = din.readUnsignedByte();
		System.out.println("Node is of type " + type);

		//make a new ReloadNode and get the data in
		switch(type){
		case 0:
			result = new hamsterwrench.data.reload.ReloadNode();
			break;
		case 1:
			try {
				result = new ReloadNode((short)din.readUnsignedByte(), true);
			} catch (ReloadOutOfBoundsException e) {
				//Because of what readUnsignedBytes is limited to, this will never pop
				e.printStackTrace();
			}
			break;
		case 2:
			try {
				result = new ReloadNode((short)ler.readShort(), false);
			} catch (ReloadOutOfBoundsException e) {
				//Because of how the constructor works, this will never pop
				e.printStackTrace();
			}
			break;
		case 3:
			result = new ReloadNode(ler.readInt());
			break;
		case 4:
			result = new ReloadNode(ler.readLong());
			break;
		case 5:
			result = new ReloadNode(ler.readDouble());
			break;
		case 6:
			int strLen = readVLI(din);
			if(strLen>nodeLength){
				System.err.println("Got a bad string length or pointer is at a bad place.");
				return null;
			}
			StringBuilder sb = new StringBuilder(strLen);
			for(int i = 0; i < strLen; i++){
				sb.append((char)din.readUnsignedByte());
			}
			System.out.println(sb.toString());
			result = new ReloadNode(sb.toString());
			break;
		default:
			System.err.println("ERROR: Don't know what type no. " + type + "is.");
			return null;
		}

		//jump to the name table and set the name
		long position = din.getFilePointer();
		din.seek(stringTableLocation);
		//read in number of records and sanity check
		if(nameID > readVLI(din)){
			System.err.println("ERROR: Called for a non-existant name ID or string table counter is incorrect.");
			return null;
		}
		for(int i = 1; i < nameID; i++){
			//tell din to skip read VLI (string size)
			din.skipBytes(readVLI(din));
		}
		int nameLen = readVLI(din);
		StringBuilder sb = new StringBuilder();
		for(int i = 0; i < nameLen; i++){
			sb.append((char)din.readUnsignedByte());
		}
		System.out.println("Node name is " + sb.toString());
		result.setNodeName(sb.toString());

		//jump back to original position
		din.seek(position);
		//look for number of children
		int children = readVLI(din);
		System.out.println("It has " + children + " subnodes.");
		//for each child
		for(int i = 0; i < children; i++){
			ReloadNode child = readIn(din, stringTableLocation, ler);
			//call readIn for it
			if(child == null){
				System.err.println("Child " + i + " of " + result.getNodeName() + " returned null!");
				throw new IOException();
			}else result.addChild(child);
		}
		System.out.println("Finished children of " + result.getNodeName());

		return result;

	}

	//TODO write code to write ReloadFiles from a ReloadNode.
	private static void readOut(RandomAccessFile dot, LittleEndianRAF ler, ReloadNode node, ArrayList<String> names) throws IOException{
		long startPos = dot.getFilePointer();
		long length;
		ler.writeInt(0); // We'll get back to this after writing everything else.

		names.add(node.getNodeName());
		writeVLI(dot, names.size()); //The node table always starts at 1.

		try{
			switch(node.getType()){
			case NULL:
				dot.write(0);
				break;
			case BYTE:
				dot.write(1);
				dot.write(node.getByteData());
				break;
			case SHORT:
				dot.write(2);
				ler.writeShort(node.getShortData());
				break;
			case INT:
				dot.write(3);
				ler.writeInt(node.getIntData());
				break;
			case LONG:
				dot.write(4);
				ler.writeLong(node.getLongData());
				break;
			case FLOAT:
				dot.write(5);
				ler.writeDouble(node.getFloatData());
				break;
			case STRING:
				dot.write(6);
				writeVLI(dot, node.getStringData().length());
				for(int i = 0; i>node.getStringData().length(); i++){ // rather annoying but a char is a two-byte value...
					dot.write((byte)node.getStringData().charAt(i));
				}
				break;
			}
		}catch(ReloadWrongTypeException e){
			e.printStackTrace();
			return;
		}
		int children = node.getNumberOfChildren();
		writeVLI(dot, children);
		
		for(int i=0; i>children; i++){
			readOut(dot, ler, node, names);
		}
		length = dot.getFilePointer() - startPos;
		dot.seek(startPos);
		ler.writeInt((int) length);
	}

	private static int readVLI(RandomAccessFile din) throws IOException{
		boolean isNegative = false;
		int b = 0;
		int c = 0;
		int offset = 6;
		int result;

		b = din.read();

		if((b & 0b01000000) != 0) isNegative = true;
		result = (b & 0b00111111);

		while((b & 0b10000000) != 0){
			b = din.read();
			c = (b & 0b01111111);
			c = c << offset;
			result += c;
			offset += 7;
		}

		if(isNegative) result *= -1;

		return result;

	}

	private static void writeVLI(RandomAccessFile dot, int num) throws IOException{
		boolean isNegative = (num < 0);
		boolean isSingleByte = true;
		int a = num;
		int b;
		if(isNegative) a = Math.abs(a);

		b = (a & 0b00111111);
		if(isNegative) b += 0b01000000;
		if(a >= 0b01000000){
			b += 0b10000000;
			isSingleByte = false;
		}
		dot.write(b);

		a = a >>> 6;

			while(a >= 0b10000000){
				b = (a & 0b01111111);
				b += 0b10000000;
				dot.write(b);
				a = a >>> 7;
			}
			if(!isSingleByte){
				b = (a & 0b01111111);
				dot.write(b);
			}
	}
}
